Las enfermedades renales crónicas representan un problema de salud
pública con un impacto creciente en Europa.
Al mismo tiempo, la calidad del agua puede verse afectada por diversos
factores:
La combinación de ambos dominios —medio ambiente y salud— permite estudiar si existe algún tipo de relación que pueda ser relevante para la prevención y gestión sanitaria.
El objetivo principal de este seminario es analizar la posible relación entre:
Para ello combinamos información procedente de bases de datos ambientales y sanitarias oficiales de la Unión Europea. Nuestro propósito es detectar patrones, correlaciones y tendencias que ayuden a comprender si la calidad del agua podría tener algún impacto sobre la salud renal de la población.
Los datos utilizados en este seminario provienen de dos fuentes oficiales europeas. Por un lado, los datos médicos proceden de Eurostat, accesibles mediante API.
Por otro lado, los datos medioambientales se han obtenido de la European Environment Agency (EEA) a través de su portal Data and Maps, donde pueden descargarse en formatos CSV, TSV, JSON y GeoJSON. Estos archivos se encuentran organizados dentro de la carpeta Data del repositorio para su posterior tratamiento en R.
Para poder utilizar estos conjuntos de datos y poder importarlos, utilizaremos las siguientes librerías:
library(eurostat)
library(xml2)
library(rjson)
library(dplyr)
library(ggplot2)
library(tidyr)
library(jsonlite)
library(furrr)
library(purrr)
library(future.apply)
library(plotly)
Con todas las librerías cargadas ya podemos importar los datos.
Importamos desde Eurostat un conjunto de datos oficiales sobre enfermedades renales, descargado automáticamente mediante su API. La función obtiene el dataset identificado como el segundo resultado relacionado con “renal” y lo carga en formato dataframe listo para analizar.
datos_nef_valores <- get_eurostat(search_eurostat("renal")[2], lang = "en")
Una vez obtenidos los datos y pudiendo ver su estructura, los modificaremos y ordenaremos de modo que sea más sencillo su manejo y comprensión.
El objetivo de este bloque de datos es conseguir que los años (TIME_PERIOD), que de forma predeterminada vienen en una única columna (date), estén correctamente formateados como valores numéricos y se puedan utilizar como variable temporal. Además, se renombran las columnas para que tengan nombres claros (icd9cm a enfermedades) y se eliminan columnas innecesarias (freq). Todo esto permite tener un dataframe limpio y estructurado, con cada registro asociado a un país y a un año, listo para su análisis.
#Eliminamos la columna freq, que solo tiene un nivel que se repite constantemente
datos_nef_valores$freq <- NULL
#Cambiamos el nombre a la columna icd9cm
datos_nef_valores <- datos_nef_valores %>%
rename(enfermedades = icd9cm)
#Cambiamos el nombre a la columna TIME_PERIOD
datos_nef_valores <- datos_nef_valores %>%
rename(date = TIME_PERIOD)
#Cambiamos formato de fecha
datos_nef_valores$date <- format(as.Date(datos_nef_valores$date), "%Y")
#Convertimos date a numeric
datos_nef_valores$date <- as.numeric(datos_nef_valores$date)
datos_nef_codigos <- get_eurostat(search_eurostat("renal")[2], lang = "en", type = "label")
datos_nef_codigos$freq <- NULL
datos_nef_codigos <- datos_nef_codigos %>%
rename(enfermedades = icd9cm)
datos_nef_codigos <- datos_nef_codigos %>%
rename(date = TIME_PERIOD)
datos_nef_codigos$date <- format(as.Date(datos_nef_codigos$date), "%Y")
datos_nef_codigos$date <- as.numeric(datos_nef_codigos$date)
#Visualización de estructura
str(datos_nef_valores)
## tibble [2,795 × 5] (S3: tbl_df/tbl/data.frame)
## $ unit : chr [1:2795] "NR" "NR" "NR" "NR" ...
## $ enfermedades: chr [1:2795] "CM3995" "CM3995" "CM3995" "CM3995" ...
## $ geo : chr [1:2795] "AT" "AT" "AT" "AT" ...
## $ date : num [1:2795] 1980 1981 1982 1983 1984 ...
## $ values : num [1:2795] 787 915 990 1158 1214 ...
str(datos_nef_codigos)
## tibble [2,795 × 5] (S3: tbl_df/tbl/data.frame)
## $ unit : chr [1:2795] "Number" "Number" "Number" "Number" ...
## $ enfermedades: chr [1:2795] "Haemodialysis" "Haemodialysis" "Haemodialysis" "Haemodialysis" ...
## $ geo : chr [1:2795] "Austria" "Austria" "Austria" "Austria" ...
## $ date : num [1:2795] 1980 1981 1982 1983 1984 ...
## $ values : num [1:2795] 787 915 990 1158 1214 ...
Importamos los datos de calidad del agua desde un archivo GeoJSON y los convertimos en un dataframe limpio y manejable en R. Primero, se leen todas las features del archivo, y para cada una se extraen sus propiedades (properties). Se reemplazan los valores NULL por NA para evitar problemas en el análisis y se convierten las listas resultantes en dataframes individuales. Finalmente, todos estos dataframes se combinan en un único dataframe (agua_data) que contiene todos los registros y atributos de interés, listo para ser filtrado, renombrado y analizado.
agua_data <- jsonlite::fromJSON("Data/DataExtract.geojson", simplifyVector = FALSE)
agua_data <- lapply(agua_data$features, function(feature){
pro <- feature$properties
pro <- lapply(pro, function(x) if (is.null(x)) NA else x)
as.data.frame(pro, stringAsFactor = FALSE)
}) %>%
bind_rows(.)
En este bloque se seleccionan únicamente las columnas que son relevantes para nuestro análisis de calidad del agua, eliminando información innecesaria. Posteriormente, se renombra la columna cArea como “Area_(km2)” para reflejar claramente que representa la superficie en kilómetros cuadrados. Finalmente, se filtran los registros eliminando las masas de agua costera (“CW”) y las de agua marítima territorial (“TeW”), ya que no son tipos de agua potable, por lo que interfieren en nuestro análisis.
agua_data <- select(agua_data, cYear, countryCode, fileUrl, euRBDCode, rbdName, euSubUnitCode, surfaceWaterBodyName, cArea, surfaceWaterBodyCategory,
reservoir, hasDescriptiveData, swEcologicalStatusOrPotentialValue, swChemicalStatusValue) %>%
dplyr::rename(., "Area_(km2)" = cArea) %>%
filter(!surfaceWaterBodyCategory %in% c("CW", "TeW"))
#Visualización de estructura
str(agua_data)
## 'data.frame': 278507 obs. of 13 variables:
## $ cYear : int 2016 2016 2016 2016 2016 2016 2016 2016 2016 2016 ...
## $ countryCode : chr "NO" "NO" "NO" "NO" ...
## $ fileUrl : chr "http://cdr.eionet.europa.eu/no/eu/wfd2016/districts/no5101/envwvqesq/SWB_NO_20180430.xml" "http://cdr.eionet.europa.eu/no/eu/wfd2016/districts/no5101/envwvqesq/SWB_NO_20180430.xml" "http://cdr.eionet.europa.eu/no/eu/wfd2016/districts/no5101/envwvqesq/SWB_NO_20180430.xml" "http://cdr.eionet.europa.eu/no/eu/wfd2016/districts/no5101/envwvqesq/SWB_NO_20180430.xml" ...
## $ euRBDCode : chr "NO5107" "NO5107" "NO5107" "NO5107" ...
## $ rbdName : chr "INNLANDET AND VIKEN" "INNLANDET AND VIKEN" "INNLANDET AND VIKEN" "INNLANDET AND VIKEN" ...
## $ euSubUnitCode : chr "NO5107" "NO5107" "NO5107" "NO5107" ...
## $ surfaceWaterBodyName : chr "STORHAAEN" "FJELLHAMARELVA - SAGELVA" "STIKKILLEN" "SIDEBEKKER TIL LEIRA NEDSTROEMS KROKFOSS" ...
## $ Area_(km2) : num 0.755 NA 0.953 NA 0.982 NA 0.535 NA 0.567 NA ...
## $ surfaceWaterBodyCategory : chr "LW" "RW" "LW" "RW" ...
## $ reservoir : chr "Inapplicable" "Inapplicable" "Inapplicable" "Inapplicable" ...
## $ hasDescriptiveData : int 1 1 1 1 1 1 1 1 1 1 ...
## $ swEcologicalStatusOrPotentialValue: chr "2" "5" "4" "4" ...
## $ swChemicalStatusValue : chr "Unknown" "Unknown" "Unknown" "Unknown" ...
colnames(agua_data)
## [1] "cYear" "countryCode"
## [3] "fileUrl" "euRBDCode"
## [5] "rbdName" "euSubUnitCode"
## [7] "surfaceWaterBodyName" "Area_(km2)"
## [9] "surfaceWaterBodyCategory" "reservoir"
## [11] "hasDescriptiveData" "swEcologicalStatusOrPotentialValue"
## [13] "swChemicalStatusValue"
Además, debido a la gran carga de datos y el tiempo de ejecución que supone, lo guardamos exportamos para optimizar el código.
# Guardamos el objeto de R para que en futuras ejecuciones no tarde tanto
saveRDS(object = agua_data, file = "Data/Datos_calidad_agua.rds")
# Carga de los datos SIN EJECUTAR TODO
agua_data <- readRDS(file = "Data/Datos_calidad_agua.rds")
Realizamos diversas comprobaciones y uniones para ver qué datos geográficos podemos utilizar como punto de unión para el análisis, y así poder compararlos con los datos del agua.
#COMPARACION DE PAISES ENTRE AMBAS BASES DE DATOS
#Vemos que paises tenemos en cada base de datos
unique(agua_data$countryCode)
## [1] "NO" "FI" "PL" "PT" "RO" "SE" "IS" "SI" "SK" "UK" "IT" "LT" "LV" "FR" "HR"
## [16] "HU" "IE" "LU" "ES" "EE" "EL" "DK" "AT" "BE" "BG" "CY" "DE" "CZ" "NL" "MT"
unique(datos_nef_valores$geo)
## [1] "AT" "BE" "BG" "CH" "CZ" "DE" "DK" "EL" "ES" "FI" "FR" "HR" "HU" "IE" "IS"
## [16] "IT" "LI" "LT" "LU" "MT" "NL" "PL" "PT" "RO" "RS" "SK" "TR" "UK" "EE"
paises_agua <- unique(agua_data$countryCode)
paises_nefro <- unique(datos_nef_valores$geo)
#Hacemos la interseccion para ver cuales son comunes
paises_comunes <- intersect(paises_agua, paises_nefro)
paises_comunes
## [1] "FI" "PL" "PT" "RO" "IS" "SK" "UK" "IT" "LT" "FR" "HR" "HU" "IE" "LU" "ES"
## [16] "EE" "EL" "DK" "AT" "BE" "BG" "DE" "CZ" "NL" "MT"
#Filtramos SOLO los países comunes
agua_data <- agua_data %>% filter(countryCode %in% paises_comunes)
datos_nef_valores <- datos_nef_valores %>% filter(geo %in% paises_comunes)
str(datos_nef_valores)
## tibble [2,615 × 5] (S3: tbl_df/tbl/data.frame)
## $ unit : chr [1:2615] "NR" "NR" "NR" "NR" ...
## $ enfermedades: chr [1:2615] "CM3995" "CM3995" "CM3995" "CM3995" ...
## $ geo : chr [1:2615] "AT" "AT" "AT" "AT" ...
## $ date : num [1:2615] 1980 1981 1982 1983 1984 ...
## $ values : num [1:2615] 787 915 990 1158 1214 ...
str(agua_data)
## 'data.frame': 174880 obs. of 13 variables:
## $ cYear : int 2022 2022 2022 2022 2022 2022 2022 2022 2022 2022 ...
## $ countryCode : chr "FI" "FI" "FI" "FI" ...
## $ fileUrl : chr NA NA NA NA ...
## $ euRBDCode : chr "FIVHA1" "FIVHA1" "FIVHA1" "FIVHA1" ...
## $ rbdName : chr "VUOKSI RIVER BASIN DISTRICT" "VUOKSI RIVER BASIN DISTRICT" "VUOKSI RIVER BASIN DISTRICT" "VUOKSI RIVER BASIN DISTRICT" ...
## $ euSubUnitCode : chr "FIVHA1" "FIVHA1" "FIVHA1" "FIVHA1" ...
## $ surfaceWaterBodyName : chr "KOTKATVESI" "VALKEINEN" "ALANNE" "ALA-LUOSTA" ...
## $ Area_(km2) : num 13.304 0.562 10.01 9.72 1.578 ...
## $ surfaceWaterBodyCategory : chr NA NA NA NA ...
## $ reservoir : chr NA NA NA NA ...
## $ hasDescriptiveData : int 0 0 0 0 0 0 0 0 0 0 ...
## $ swEcologicalStatusOrPotentialValue: chr NA NA NA NA ...
## $ swChemicalStatusValue : chr NA NA NA NA ...
# Unión
datos_combinados <- right_join(agua_data, datos_nef_valores,
by = c("countryCode" = "geo"))
## Warning in right_join(agua_data, datos_nef_valores, by = c(countryCode = "geo")): Detected an unexpected many-to-many relationship between `x` and `y`.
## ℹ Row 1 of `x` matches multiple rows in `y`.
## ℹ Row 174 of `y` matches multiple rows in `x`.
## ℹ If a many-to-many relationship is expected, set `relationship =
## "many-to-many"` to silence this warning.
str(datos_combinados)
## 'data.frame': 22095218 obs. of 17 variables:
## $ cYear : int 2022 2022 2022 2022 2022 2022 2022 2022 2022 2022 ...
## $ countryCode : chr "FI" "FI" "FI" "FI" ...
## $ fileUrl : chr NA NA NA NA ...
## $ euRBDCode : chr "FIVHA1" "FIVHA1" "FIVHA1" "FIVHA1" ...
## $ rbdName : chr "VUOKSI RIVER BASIN DISTRICT" "VUOKSI RIVER BASIN DISTRICT" "VUOKSI RIVER BASIN DISTRICT" "VUOKSI RIVER BASIN DISTRICT" ...
## $ euSubUnitCode : chr "FIVHA1" "FIVHA1" "FIVHA1" "FIVHA1" ...
## $ surfaceWaterBodyName : chr "KOTKATVESI" "KOTKATVESI" "KOTKATVESI" "KOTKATVESI" ...
## $ Area_(km2) : num 13.3 13.3 13.3 13.3 13.3 ...
## $ surfaceWaterBodyCategory : chr NA NA NA NA ...
## $ reservoir : chr NA NA NA NA ...
## $ hasDescriptiveData : int 0 0 0 0 0 0 0 0 0 0 ...
## $ swEcologicalStatusOrPotentialValue: chr NA NA NA NA ...
## $ swChemicalStatusValue : chr NA NA NA NA ...
## $ unit : chr "NR" "NR" "NR" "NR" ...
## $ enfermedades : chr "CM3995" "CM3995" "CM3995" "CM3995" ...
## $ date : num 1980 1981 1982 1983 1984 ...
## $ values : num 223 256 305 398 395 458 463 514 542 554 ...
#Resumen datos de agua por país
agua_resumen <- agua_data %>%
group_by(countryCode) %>%
summarise(
Area_total = sum(`Area_(km2)`, na.rm = TRUE),
swEcologicalStatus_promedio = mean(
as.numeric(swEcologicalStatusOrPotentialValue[grepl("^[0-9]+$", swEcologicalStatusOrPotentialValue)]),
na.rm = TRUE
),
swChemicalStatus_promedio = mean(
as.numeric(swChemicalStatusValue[grepl("^[0-9]+$", swChemicalStatusValue)]),
na.rm = TRUE
)
)
#Resumen datos renales por país
nefro_resumen <- datos_nef_valores %>%
group_by(geo) %>%
summarise(total_casos_renales = sum(values, na.rm = TRUE))
#Unión final de los datos resumidos
datos_combinados_final <- agua_resumen %>%
full_join(nefro_resumen, by = c("countryCode" = "geo"))
str(datos_combinados_final)
## tibble [25 × 5] (S3: tbl_df/tbl/data.frame)
## $ countryCode : chr [1:25] "AT" "BE" "BG" "CZ" ...
## $ Area_total : num [1:25] 1046 183 1040 544 6427 ...
## $ swEcologicalStatus_promedio: num [1:25] 2.48 3.4 2.8 3.27 3.64 ...
## $ swChemicalStatus_promedio : num [1:25] 3 2.99 2.17 2.41 3 ...
## $ total_casos_renales : num [1:25] 340313 213327 16683 202177 1815105 ...
View(datos_combinados_final)
Mediante el uso de diferentes visualizaciones, analizaremos cómo varían las características del agua en cada país y evaluaremos si dichos cambios pueden estar relacionados con un mayor riesgo de desarrollar enfermedades renales. A través de estas gráficas podremos observar patrones, tendencias y posibles asociaciones entre la calidad del agua y la incidencia de casos renales, lo que nos permitirá comprender mejor la influencia del entorno hídrico en la salud de la población.
#Casos renales vs estado ecológico del agua
ggplot(datos_combinados_final, aes(x = swEcologicalStatus_promedio, y = total_casos_renales)) +
geom_point(aes(size = Area_total, color = countryCode)) +
geom_smooth(method = "lm", se = TRUE, color = "black") +
geom_smooth(method = "loess", se = TRUE, color = "red") +
labs(
x = "Estado ecológico promedio del agua",
y = "Total de casos renales",
size = "Área total (km2)",
color = "País"
) +
theme_minimal()
## Warning: Removed 1 row containing non-finite outside the scale range (`stat_smooth()`).
## Removed 1 row containing non-finite outside the scale range (`stat_smooth()`).
## Warning: Removed 1 row containing missing values or values outside the scale range
## (`geom_point()`).
En este gráfico, la relación lineal entre el estado químico del agua y
la tasa de casos renales por 100 mil habitantes es débil y ligeramente
negativa. Esto indica que, en promedio, mejorar el estado químico del
agua no se asocia de forma clara con una reducción de enfermedades
renales, al menos no bajo un supuesto estrictamente lineal.
La curva LOESS vuelve a mostrar una forma en U, más marcada que en el gráfico del estado ecológico: las tasas más bajas de casos renales aparecen en niveles intermedios del estado químico, mientras que los valores extremos (muy pobres o muy buenos) muestran incrementos en la enfermedad renal. Esta relación no lineal sugiere que los extremos del estado químico podrían estar capturando condiciones específicas de ciertos países, posibles factores no controlados en el análisis o por poca densidad de datos en los extremos del eje.
#Casos renales vs estado químico del agua
ggplot(datos_combinados_final, aes(x = swChemicalStatus_promedio, y = total_casos_renales)) +
geom_point(aes(size = Area_total, color = countryCode)) +
geom_smooth(method = "lm", se = TRUE, color = "black") +
geom_smooth(method = "loess", se = TRUE, color = "red") +
labs(
x = "Estado químico promedio del agua",
y = "Total de casos renales",
size = "Área total (km2)",
color = "País"
) +
theme_minimal()
## Warning: Removed 1 row containing non-finite outside the scale range (`stat_smooth()`).
## Removed 1 row containing non-finite outside the scale range (`stat_smooth()`).
## Warning: Removed 1 row containing missing values or values outside the scale range
## (`geom_point()`).
El gráfico muestra que no existe una relación lineal fuerte entre el
estado ecológico del agua y la tasa de casos renales por 100 mil
habitantes. La pendiente del ajuste lineal es ligeramente negativa, lo
que sugiere una tendencia muy débil hacia menos casos cuando el estado
ecológico mejora. Sin embargo, la variabilidad entre países sigue siendo
grande y no se observa un patrón simple que explique los valores más
altos de enfermedades renales.
La curva LOESS revela una relación no lineal con forma de U, donde los valores intermedios del estado ecológico del agua (aprox. 2.4–2.6) se asocian con las tasas más bajas de casos renales, mientras que tanto los niveles muy bajos como los muy altos se relacionan con tasas mayores. Como en el caso anterior, puede deberse a diversas causas no relacionadas con el análisis principal.
#GRÁFICO ESTADO QUÍMICO Y ECOLÓGICO VS CASOS RENALES.
# Crear el gráfico de burbujas
p <- ggplot(datos_combinados_final, aes(
x = swChemicalStatus_promedio, # eje x = estado químico
y = swEcologicalStatus_promedio, # eje y = estado ecológico
size = total_casos_renales, # tamaño = casos renales
color = swChemicalStatus_promedio, # color opcional
text = paste(
"<b>", countryCode, "</b><br>",
"Casos renales: ", total_casos_renales, "<br>",
"Estado ecológico: ", round(swEcologicalStatus_promedio,2), "<br>",
"Estado químico: ", round(swChemicalStatus_promedio,2)
)
)) +
geom_point(alpha = 0.8) +
scale_size_continuous(range = c(5, 25)) + # Ajusta según la magnitud de los datos
scale_color_viridis_c(option = "plasma", na.value = "lightgrey") +
theme_minimal() +
labs(
x = "Estado químico",
y = "Estado ecológico",
size = "Casos renales",
color = "Estado químico"
)
# Convertir a interactivo
p_interactivo <- ggplotly(p, tooltip = "text")
p_interactivo
En este caso podemos ver los datos analizados anteriormente con más precisión, pero de una forma más interactiva y visual para el análisis.
#GRÁFICO ESTADO QUÍMICO Y ECOLÓGICO VS CASOS RENALES
# Seleccionar solo las columnas numéricas relevantes
datos_corr <- datos_combinados_final %>%
select(
swEcologicalStatus_promedio,
swChemicalStatus_promedio,
Area_total,
total_casos_renales
)
# Calcular matriz de correlaciones
matriz_corr <- round(cor(datos_corr, use = "pairwise.complete.obs"), 2)
# Pasamos a formato largo
matriz_corr_long <- as.data.frame(matriz_corr) %>%
mutate(Var1 = rownames(.)) %>%
pivot_longer(-Var1, names_to = "Var2", values_to = "Correlacion")
# Heatmap
ggplot(matriz_corr_long, aes(x = Var1, y = Var2, fill = Correlacion)) +
geom_tile(color = "white") +
geom_text(aes(label = Correlacion), color = "black", size = 5) +
scale_fill_viridis_c(option = "plasma", limits = c(-1,1)) +
theme_minimal(base_size = 13) +
theme(
axis.text.x = element_text(angle = 45, hjust = 1),
axis.title = element_blank(),
panel.grid = element_blank()
) +
labs(
fill = "Correlación",
title = "Heatmap de correlaciones entre calidad del agua y casos renales"
)
Finalmente, en este último gráfico podemos observar los valores de forma
numérica. Este análisis de correlaciones confirma que, en este conjunto
de datos, no existe una relación lineal significativa entre la calidad
del agua y la carga de enfermedad renal por país. Tanto el estado
ecológico como el químico muestran correlaciones cercanas a cero con los
casos renales (-0.03 y -0.09), lo que indica ausencia de asociación
lineal detectable. Del mismo modo, el área total de cada país tampoco
guarda relación con la enfermedad renal (-0.02), descartando que el
tamaño territorial influya en los resultados. La única correlación
relevante es la moderada relación entre la calidad ecológica y química
del agua (0.54), algo esperable dado que ambos indicadores suelen
mejorar o deteriorarse conjuntamente. En conjunto, el gráfico sugiere
que la calidad del agua, medida de forma lineal y agregada a nivel país,
no explica las variaciones en la tasa de enfermedad renal —lo que apunta
a que otros factores demográficos, socioeconómicos o ambientales podrían
estar desempeñando un papel más importante.
Tras el análisis detallado de los datos y la observación de las gráficas proporcionadas, se puede inferir que, en el contexto de los países europeos, la calidad del agua no parece tener un impacto significativo en la incidencia de enfermedades renales. Esto sugiere que otros factores, posiblemente relacionados con hábitos de vida, genética, atención médica o condiciones socioeconómicas, podrían desempeñar un papel más determinante en la aparición de estas patologías. Por lo tanto, aunque la calidad del agua sigue siendo un aspecto importante para la salud pública en general, en este caso específico no se observa una relación directa y significativa con los problemas renales.